using System;
using System.Collections.Generic;
using System.IO;
using System.Security.Cryptography;
using System.Text;

namespace Kiiro.Common.Utility
{
    public class KiiroEncryption
    {
        #region Singleton

        private static readonly object threadLock = new object ( );
        private static KiiroEncryption instance = null;

        private KiiroEncryption ( )
        {
        }

        public static KiiroEncryption Instance
        {
            get
            {
                lock ( threadLock )
                {
                    if ( instance == null )
                    {
                        instance = new KiiroEncryption ( );
                    }
                    return instance;
                }
            }
        }

        #endregion

        /// <summary>
        /// Returns encrypted value based on passed in string to be encrypted and public key.
        /// </summary>
        /// <param name="sourceToBeEncrypted"></param>
        /// <param name="key">An 8 character string.</param>
        /// <returns></returns>
        public string Encrypt ( string sourceToBeEncrypted, string key )
        {
            string returnValue = string.Empty;

            if ( key.Trim ( ).Length <= 8 )
            {
                key.PadRight ( 8, '0' );
                //Get binaries of source and key:
                byte [ ] binarySource = ASCIIEncoding.ASCII.GetBytes ( sourceToBeEncrypted );
                MemoryStream memoryStream = new MemoryStream ( );
                byte [ ] binaryKey = GetBinaryKey ( key );

                //Set the key to the DES key and initialization vector:
                DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider ( );
                desProvider.Key = binaryKey;
                desProvider.IV = binaryKey;

                ICryptoTransform encryptor = desProvider.CreateEncryptor ( );

                //Write the encrypted source to the memorystream:
                CryptoStream cryptoStream = new CryptoStream ( memoryStream, encryptor, CryptoStreamMode.Write );
                cryptoStream.Write ( binarySource, 0, binarySource.Length );
                cryptoStream.FlushFinalBlock ( );

                byte [ ] binaryOutput = memoryStream.GetBuffer ( );
                int i = 0;

                //get the index of the ending zeros to trim the byte array
                for ( i = binaryOutput.Length - 1; i > 0; i-- )
                {
                    if ( binaryOutput [ i ] != 0 )
                    {
                        break;
                    }
                }

                //return the encrypted source:
                returnValue = Convert.ToBase64String ( binaryOutput, 0, i + 1 );
            }
            else
            {
                throw new Exception ( "Invalid key length. Key must be 8 characters or less in length." );
            }

            return returnValue;
        }

        /// <summary>
        /// Returns decrypted string based on encr
        /// </summary>
        /// <param name="decryptionSource"></param>
        /// <param name="key">An 8 character string.</param>
        /// <returns></returns>
        public string Decrypt ( string sourceToBeDecrypted, string key )
        {
            string returnValue = string.Empty;

            if ( key.Trim ( ).Length <= 8 )
            {
                key.PadRight ( 8, '0' );

                //Get binaries of source and key:
                byte [ ] binaryInput = Convert.FromBase64String ( sourceToBeDecrypted );
                MemoryStream memoryStream = new MemoryStream ( binaryInput, 0, binaryInput.Length );
                byte [ ] binaryKey = GetBinaryKey ( key );

                //Set the key to the DES key and initialization vector:
                DESCryptoServiceProvider desProvider = new DESCryptoServiceProvider ( );
                desProvider.Key = binaryKey;
                desProvider.IV = binaryKey;

                ICryptoTransform decryptor = desProvider.CreateDecryptor ( );

                CryptoStream cryptoStream = new CryptoStream ( memoryStream, decryptor, CryptoStreamMode.Read );

                StreamReader streamReader = new StreamReader ( cryptoStream );

                try
                {
                    //Return the decrypted string:
                    returnValue = streamReader.ReadToEnd ( );
                }
                catch
                {
                    returnValue = string.Empty;
                }
            }
            else
            {
                throw new Exception ( "Invalid key length. Key must be 8 characters or less in length." );
            }

            return returnValue;
        }

        private byte [ ] GetBinaryKey ( string key )
        {
            return ASCIIEncoding.ASCII.GetBytes ( key );
        }

        public string TripleDESEncrypt ( string decrypted )
        {
            byte [ ] key = Encoding.ASCII.GetBytes ( Constants.Security.EK );
            byte [ ] iv = Encoding.ASCII.GetBytes ( Constants.Security.HK );
            byte [ ] data = Encoding.ASCII.GetBytes ( decrypted );
            //byte [ ] data = Encoding.ASCII.GetBytes ( "OpenProjects|1;ActiveUsers|1" );

            byte [ ] enc = new byte [ 0 ];
            TripleDES tdes = TripleDES.Create ( );
            tdes.IV = iv;
            tdes.Key = key;
            tdes.Mode = CipherMode.CBC;
            tdes.Padding = PaddingMode.Zeros;
            ICryptoTransform ict = tdes.CreateEncryptor ( );
            
            enc = ict.TransformFinalBlock ( data, 0, data.Length );
            string encrypted = Bin2Hex ( enc );
            
            return encrypted;
        }

        public string TripleDESDecrypt ( string encrypted )
        {
            byte [ ] key = Encoding.ASCII.GetBytes ( Constants.Security.EK );
            byte [ ] iv = Encoding.ASCII.GetBytes ( Constants.Security.HK );
            byte [ ] data = Hex2Bin ( encrypted );
            //byte [ ] data = Hex2Bin ( "b1e960c1ff33f176cb7c2bb3d8c0382e0fa11b21a14bfa5b677f0e3f78414777" );

            MemoryStream msDecrypt = new MemoryStream ( data );

            TripleDES tdes = TripleDES.Create ( );
            tdes.IV = iv;
            tdes.Key = key;
            tdes.Mode = CipherMode.CBC;
            tdes.Padding = PaddingMode.Zeros;
            ICryptoTransform ict = tdes.CreateDecryptor ( );

            CryptoStream csDecrypt = new CryptoStream ( msDecrypt, ict, CryptoStreamMode.Read );
            byte [ ] fromEncrypt = new byte [ data.Length ];
            csDecrypt.Read ( fromEncrypt, 0, fromEncrypt.Length );
            string decrypted = new ASCIIEncoding ( ).GetString ( fromEncrypt ).Replace ( "\0", "" );

            return decrypted;
        }
	
        private string Bin2Hex ( byte [ ] bin )
        {
            StringBuilder sb = new StringBuilder ( bin.Length * 2 );
            foreach ( byte b in bin )
            {
                sb.Append ( b.ToString ( "x" ).PadLeft ( 2, '0' ) );
            }
            return sb.ToString ( );
        }

        private byte [ ] Hex2Bin ( string hexString )
        {
            byte [ ] bin = null;

            if ( hexString != null )
            {
                int len = hexString.Length;
                if ( len % 2 != 1 )
                {
                    int len_half = len / 2;
                    bin = new byte [ len_half ];

                    try
                    {
                        for ( int i = 0; i != len_half; i++ )
                        {
                            bin [ i ] = ( byte ) Int32.Parse ( hexString.Substring ( i * 2, 2 ), System.Globalization.NumberStyles.HexNumber );
                        }
                    }
                    catch ( Exception ex )
                    {
                    }
                }
            }

            return bin;
        }
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.Common/Utility/KiiroEncryption.cs $
 * 
 * 5     17/08/09 10:38a Camerons
 * Added check-in comments section
 * 
 ************************************************************************/